6.3 Die Methoden in einer abgeleiteten Klasse  
Klassen werden abgeleitet, um aus einer Klasse eine spezialisiertere mit weiteren, spezifischen Verhaltensweisen zu entwickeln. Dazu können in Subklassen neue Methoden implementiert werden, die über die der Basisklasse hinausgehen und Verhaltensweisen beschreiben, in der sich die Subklasse von ihrer Basisklasse unterscheidet. Die Methode Draw der Klasse GraphicCircle ist ein Beispiel dazu.
Die von der abgeleiteten Klasse aus der Basisklasse geerbten Methoden müssen nicht im Verhältnis 1:1 übernommen werden, sondern können auf abweichende Bedürfnisse oder Anforderungen angepasst werden. Dazu bietet das objektorientierte Konzept von Visual Basic das Verdecken (Ausblenden) und die Methodenüberladung an.
6.3.1 Geerbte Methoden mit »Shadows« verdecken  
Vom Verdecken oder Ausblenden einer Basisklassenmethode wird gesprochen, wenn in der abgeleiteten Klasse eine Methode implementiert wird,
|
die den gleichen Namen und |
|
eine identische Parameterliste |
besitzt wie eine Methode in der Basisklasse und diese durch eine eigene Implementierung vollständig ersetzt. Das ist beispielsweise der Fall, wenn die Implementierung in der Basisklasse für Objekte vom Typ der abgeleiteten Klasse falsch oder nicht wünschenswert ist. Wird eine Basisklassenmethode in der abgeleiteten Klasse ausgeblendet, wird beim Aufruf der Methode auf Objekte vom Typ der Subklasse immer die verdeckende Version ausgeführt. Entscheidend für das Verdecken einer geerbten Methode ist die Ergänzung der Methodendefinition um den Modifizierer Shadows. Dem VB-Compiler teilen wird damit unsere Absicht mit.
Zur Verdeutlichung nehmen wir an, in der Klasse ClassA wäre die parameterlose Methode TestMethod enthalten:
| Class ClassA
|
| Public Sub TestMethod()
|
| Console.WriteLine("TestMethod in ClassA")
|
| End Sub
|
| End Class
|
Man kann sich vorstellen, dass eine ClassB sehr wohl ein Interesse an einer Methode namens TestMethod hat – allerdings mit einer anderen Implementierung. In der abgeleiteten Klasse muss daher die geerbte Methode der Basisklasse ausgeblendet und typspezifisch neu codiert werden. Wir können jetzt in ClassB mit
| Class ClassB
|
| Inherits ClassA
|
| Public Shadows Sub TestMethod()
|
| Console.WriteLine("TestMethod in ClassB")
|
| End Sub
|
| End Class
|
eine gleichnamige Methode mit identischer Signierung bereitstellen. Da sowohl der Name als auch die Parameterliste identisch mit der aus ClassA geerbten Methode sind, verdeckt TestMethod in ClassB die geerbte.
Wird TestMethod auf ein Objekt vom Typ ClassB mit
| Dim obj As ClassB = New ClassB
|
| obj.TestMethod()
|
aufgerufen, wird die in der abgeleiteten Klasse definierte Methode ausgeführt, was im Befehlsfenster zu der Ausgabe
führt.
|
In gleicher Weise, wie eine geerbte Instanzmethode in einer ableitenden Klasse verdeckt werden kann, lassen sich mit Shadows auch Eigenschaften, Felder und statische Komponenten einer Basisklasse ausblenden und durch eine typspezifische Implementierung ersetzen.
|
Die Sichtbarkeit eines verdeckenden Klassenmitglieds
Zugriffsmodifizierer beschreiben die Sichtbarkeit eines Klassenmitglieds. Ein Public deklariertes Mitglied ist über die Grenzen der aktuellen Assemblierung hinaus bekannt, während der Modifizierer Friend die Sichtbarkeit auf die aktuelle Assemblierung beschränkt. Private Klassenmitglieder hingegen sind nur in der definierenden Klasse sichtbar.
Ein verdeckendes Member muss nicht zwangsläufig denselben Zugriffsmodifizierer haben wie das Member der Basisklasse, das verdeckt wird. Deshalb darf die öffentliche Methode TestMethod der ClassA in der erbenden Klasse ClassB durch eine Private deklarierte Methode ausgeblendet werden.
| Class ClassA
|
| Public Sub TestMethod()
|
| Console.WriteLine("TestMethod in ClassA")
|
| End Sub
|
| End Class
|
| Class ClassB
|
| Inherits ClassA
|
| Private Shadows Sub TestMethod()
|
| Console.WriteLine("TestMethod in ClassB")
|
| End Sub
|
| End Class
|
Dies führt dazu, dass die verdeckende Methode nur innerhalb von ClassB sichtbar ist, während der Aufruf von TestMethod auf ein Objekt vom Typ ClassB zu der am nächsten liegenden, gleichnamigen öffentlichen Methode in der Vererbungslinie führt:
| Dim obj As ClassB = New ClassB
|
| obj.TestMethod()
|
Im Befehlsfenster wird
ausgegeben. Wir können daraus die Schlussfolgerung ziehen:
|
Das vollständige Ausblenden eines geerbten Mitglieds durch Privatisierung ist nicht möglich.
|
Zugriff auf Basisklassenmethoden mit »MyBase«
Obwohl man die geerbte Methode einer Basisklasse verdeckt, um eine gleichnamige Methode mit klassenspezifischer Verhaltensweise in der abgeleiteten Klasse bereitzustellen, kann es unter Umständen sinnvoll sein, den Code aus der Basisklassenmethode zu nutzen, um deren Verhalten in der abgeleiteten Klasse zu übernehmen und passend zu erweitern.
Stellen wir uns dazu ein einfaches Beispiel vor. In der Klasse BaseClass sei die Methode GetNumbers definiert, die mit dem Zufallszahlengenerator Random zwei Zahlen im Bereich zwischen einschließlich 0 und 10 ermittelt und diese in Form eines Arrays an den Benutzer der Klasse zurückgibt.
| Class BaseClass
|
| Public Function GetNumbers() As Integer()
|
| Dim arr(1) As Integer
|
| Dim rnd As New Random
|
| For i As Integer = 0 To 1
|
| arr(i) = rnd.Next(11)
|
| Next
|
| Return arr
|
| End Function
|
| End Class
|
Nehmen wir an, dass eine Klasse, die aus BaseClass abgeleitet wird, eine Methode GetNumbers enthalten soll, die ebenfalls zwei Zufallszahlen aus demselben Zahlenbereich erzeugen soll, jedoch mit dem Unterschied, dass das 0-indizierte Element grundsätzlich immer die kleinere der beiden Zahlen enthalten soll.
Wir könnten in der Methode der abgeleiteten Klasse denselben Code, wie er in der Methode der Basisklasse enthalten ist, noch einmal schreiben – aber sehr elegant ist diese Lösung nicht. Besser ist es, mit dem Schlüsselwort MyBase auf das entsprechende Member in der Basisklasse zuzugreifen und dessen Verhalten zu übernehmen:
| Class DerivedClass
|
| Inherits BaseClass
|
| Public Shadows Function GetNumbers() As Integer()
|
| Dim arr() As Integer = MyBase.GetNumbers()
|
| If (arr(0) > arr(1)) Then
|
| Dim temp As Integer
|
| temp = arr(1)
|
| arr(1) = arr(0)
|
| arr(0) = temp
|
| End If
|
| Return arr
|
| End Function
|
| End Class
|
Wir hatten MyBase schon im Zusammenhang mit der Konstruktorverkettung kennen gelernt. In diesem Beispiel wird der Einsatz durch den Aufruf eines Mitglieds der direkten Basisklasse gezeigt:
| Dim arr() As Integer = MyBase.GetNumbers()
|
GetNumbers in der Basisklasse liefert als Rückgabewert ein Array vom Typ Integer, der in arr entgegengenommen wird. Falls die Reihenfolge nicht den Anforderungen entspricht, wird zunächst die Hilfsvariable temp deklariert, die eines der beiden Array-Elemente während der Umschichtung der Werte kurzfristig zwischenspeichern soll.
6.3.2 Überladen einer Basisklassenmethode  
Oft ist es notwendig, die von einer Basisklasse geerbten Methoden in der Subklasse zu überladen, um ein Objekt vom Typ der Subklasse an speziellere Anforderungen anzupassen. Von einer Methodenüberladung wird bekanntlich gesprochen, wenn sich zwei gleichnamige Methoden einer Klasse nur in ihrer Parameterliste unterscheiden. Derselbe Begriff wurde geprägt, wenn eine geerbte Methode in der Subklasse nach den Regeln der Methodenüberladung ergänzt werden muss.
Im folgenden Beispiel ist die Klasse BaseClass definiert, die eine Methode namens GetValue veröffentlicht, die einen Integer-Wert entgegennimmt und im geschützten Feld intVar speichert:
| Class BaseClass
|
| Protected intVar As Integer
|
| Public Sub GetValue(ByVal x As Integer)
|
| intVar = x
|
| End Sub
|
| End Class
|
Die Klasse DerivedClass beerbt die Klasse BaseClass, implementiert allerdings eine von der geerbten Methode GetValue abweichende Parameterliste und überlädt diese unter Berücksichtigung des Modifizierers Overloads:
| Class DerivedClass
|
| Inherits BaseClass
|
| Private lngVar As Long
|
| Public Overloads Sub GetValue(ByVal x As Integer, ByVal y As Long)
|
| intVar = x
|
| lngVar = y
|
| End Sub
|
| End Class
|
Wird mit
| Dim obj As New DerivedClass
|
ein Objekt vom Typ der abgeleiteten Klasse erzeugt, kann auf dessen Referenz mit zwei Methoden operiert werden, z. B.:
| obj.GetValue(23)
|
| obj.GetValue(12, 455)
|
|